package com.tmt.datatable;

import com.tmt.datatable.model.Column;
import com.tmt.datatable.model.Order;
import com.tmt.datatable.model.Search;
import com.tmt.helper.StringHelper;

import java.io.Serializable;
import java.text.MessageFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Datatable 的数据请求
 * <p>
 * Created by Stark on 2017/4/27.
 */
public class TableRequest implements Serializable {
    private final static Pattern pattern = Pattern.compile("\\[(.*?)\\]");
    private final static String COLUMN_DATA = "columns[{0}][data]";
    private final static String COLUMN_NAME = "columns[{0}][name]";
    private final static String COLUMN_SEARCHABLE = "columns[{0}][searchable]";
    private final static String COLUMN_ORDERABLE = "columns[{0}][orderable]";
    private final static String COLUMN_SEARCH_VALUE = "columns[{0}][search][value]";
    private final static String COLUMN_SEARCH_REGEX = "columns[{0}][search][regex]";
    private final static String ORDER_COLUMN = "order[{0}][column]";
    private final static String ORDER_DIR = "order[{0}][dir]";
    private final static String SEARCH_VALUE = "search[value]";
    private final static String SEARCH_REGEX = "search[regex]";


    /**
     * 区域开始值的后缀
     */
    private static final String SUFFIX_RANGE_START = "_start";
    /**
     * 区域结束值的后缀
     */
    private static final String SUFFIX_RANGE_END = "_end";
    /**
     * 区域时间结束值的后缀，主要是在时间上取当前日期的最后一天
     */
    private static final String SUFFIX_RANGE_END_DATE = "_endDate";

    private Integer draw = 0;
    private List<Column> columns;
    private List<Order> order;

    private Integer start = 0;
    private Integer length = 20;
    private Search search;

    /**
     * 前台自定义过滤条件
     */
    private Map<String, String[]> filter;

    public Integer getDraw() {
        return draw;
    }

    public void setDraw(Integer draw) {
        this.draw = draw;
    }

    public List<Column> getColumns() {
        return columns;
    }

    public void setColumns(List<Column> columns) {
        this.columns = columns;
    }

    public List<Order> getOrder() {
        return order;
    }

    public void setOrder(List<Order> order) {
        this.order = order;
    }

    public Integer getStart() {
        return start;
    }

    public void setStart(Integer start) {
        this.start = start;
    }

    public Integer getLength() {
        return length;
    }

    public void setLength(Integer length) {
        this.length = length;
    }

    public Search getSearch() {
        return search;
    }

    public void setSearch(Search search) {
        this.search = search;
    }

    public Map<String, String[]> getFilter() {
        return filter;
    }

    public void setFilter(Map<String, String[]> filter) {
        this.filter = filter;
    }

    /**
     * parse data table request from httpServletRequest
     *
     * @param params
     * @return
     */
    public static TableRequest from(Map<String, String[]> params) {
        TableRequest dtRequest = new TableRequest();
        dtRequest.handle(params);
        return dtRequest;
    }

    private void handle(Map<String, String[]> params) {
        setDraw(StringHelper.convertTo(params.get("draw")[0], Integer.class));
        setStart(StringHelper.convertTo(params.get("start")[0], Integer.class));
        setLength(StringHelper.convertTo(params.get("length")[0], Integer.class));

        handleColumns(params);
        handleOrder(params);
        handleSearch(params);
        handleFilter(params);
    }

    private int getMaxIndex(Stream<String> keys) {
        return keys.map(c -> {
            Matcher matcher = pattern.matcher(c);
            if (matcher.find()) {
                return matcher.group().replace("[", "").replace("]", "");
            } else {
                return "";
            }
        }).distinct().filter(c -> !c.isEmpty()).mapToInt(Integer::parseInt).boxed().max(Comparator.naturalOrder()).orElse(-1);
    }

    private void handleColumns(Map<String, String[]> params) {
        columns = new LinkedList<>();
        Map<String, String[]> map = params.entrySet().stream()
                .filter(c -> c.getKey().startsWith("columns["))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

        int maxIndex = getMaxIndex(map.keySet().stream());

        for (int i = 0; i <= maxIndex; i++) {
            Column temp = new Column();
            if (map.containsKey(MessageFormat.format(COLUMN_DATA, i))) {
                temp.setData(params.get(MessageFormat.format(COLUMN_DATA, i))[0]);
            }
            if (map.containsKey(MessageFormat.format(COLUMN_NAME, i))) {
                temp.setName(params.get(MessageFormat.format(COLUMN_NAME, i))[0]);
            }
            if (map.containsKey(MessageFormat.format(COLUMN_SEARCHABLE, i))) {
                temp.setSearchable(StringHelper.convertTo(params.get(MessageFormat.format(COLUMN_SEARCHABLE, i))[0], Boolean.class));
            }
            if (map.containsKey(MessageFormat.format(COLUMN_ORDERABLE, i))) {
                temp.setOrderable(StringHelper.convertTo(params.get(MessageFormat.format(COLUMN_ORDERABLE, i))[0], Boolean.class));
            }
            if (map.containsKey(MessageFormat.format(COLUMN_SEARCH_VALUE, i))) {
                if (temp.getSearch() == null) temp.setSearch(new Search());
                temp.getSearch().setValue(params.get(MessageFormat.format(COLUMN_SEARCH_VALUE, i))[0]);
            }
            if (map.containsKey(MessageFormat.format(COLUMN_SEARCH_REGEX, i))) {
                if (temp.getSearch() == null) temp.setSearch(new Search());
                temp.getSearch().setRegex(StringHelper.convertTo(params.get(MessageFormat.format(COLUMN_SEARCH_REGEX, i))[0], Boolean.class));
            }
            columns.add(temp);
        }
    }

    private void handleOrder(Map<String, String[]> params) {
        order = new LinkedList<>();
        Map<String, String[]> map = params.entrySet().stream()
                .filter(c -> c.getKey().startsWith("order["))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

        int maxIndex = getMaxIndex(map.keySet().stream());

        for (int i = 0; i <= maxIndex; i++) {
            Order temp = new Order();
            if (map.containsKey(MessageFormat.format(ORDER_COLUMN, i))) {
                temp.setColumn(StringHelper.convertTo(params.get(MessageFormat.format(ORDER_COLUMN, i))[0], Integer.class));
            }
            if (map.containsKey(MessageFormat.format(ORDER_DIR, i))) {
                temp.setDir(params.get(MessageFormat.format(ORDER_DIR, i))[0]);
            }

            order.add(temp);
        }
    }

    private void handleSearch(Map<String, String[]> params) {
        if (getSearch() == null) setSearch(new Search());
        getSearch().setValue(params.get(SEARCH_VALUE)[0]);
        getSearch().setRegex(StringHelper.convertTo(params.get(SEARCH_REGEX)[0], Boolean.class));

        Map<String, String[]> tmpMap =  parserCondition(params, "searcher");
        getSearch().setSearcher(tmpMap);
    }

    private void handleFilter(Map<String, String[]> params) {
        Map<String, String[]> tmpMap = parserCondition(params, "filter");
        setFilter(tmpMap);
    }

    private  Map<String, String[]>  parserCondition(Map<String, String[]> params, String conditionName) {
        Map<String, String[]> tmpMap = params.entrySet().stream()
                .filter(c -> c.getKey().startsWith(conditionName + "["))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

        //找出无效的Key。
        List<String> keys = new ArrayList<>(tmpMap.keySet());
        List<String> invalidKeys = tmpMap.keySet().stream()
                .filter(x -> x.endsWith("[]") && keys.contains(x.substring(0, x.length() - 2)))
                .collect(Collectors.toList());
        invalidKeys.forEach(tmpMap::remove);

        //获得所有的过滤条件。
        Map<String, String[]> map = tmpMap.entrySet().stream().collect(Collectors.toMap(c -> c.getKey()
                .replace(conditionName + "[", "")
                .replace("[", "")
                .replace("]", ""), Map.Entry::getValue));

        //移除范围过滤条件。
        Map<String, String[]> removedMap = map.entrySet().stream()
                .filter(x -> x.getKey().endsWith(SUFFIX_RANGE_START)
                        || x.getKey().endsWith(SUFFIX_RANGE_END)
                        || x.getKey().endsWith(SUFFIX_RANGE_END_DATE))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
        removedMap.keySet().forEach(x -> map.remove(x));

        //重新添加范围过滤条件。
        List<String> propNames = removedMap.keySet().stream()
                .map(x ->x.replace(SUFFIX_RANGE_START, "")
                        .replace(SUFFIX_RANGE_END_DATE, "")
                        .replace(SUFFIX_RANGE_END, ""))
                .distinct()
                .collect(Collectors.toList());

        propNames.forEach(x -> {
            Optional<Map.Entry<String, String[]>> start = removedMap.entrySet().stream().filter(y -> Objects.equals(y.getKey(), x + SUFFIX_RANGE_START)).findFirst();
            Optional<Map.Entry<String, String[]>> end = removedMap.entrySet().stream().filter(y -> Objects.equals(y.getKey(), x + SUFFIX_RANGE_END)
                    || Objects.equals(y.getKey(), x + SUFFIX_RANGE_END_DATE)).findFirst();
            String[] value = new String[2];
            value[0] = start.isPresent() ? start.get().getValue()[0] : "";
            value[1] = end.isPresent() ? end.get().getValue()[0] : "";
            map.put(x, value);
        });

        return map;
    }
}
